--region NewFile_1.lua
--Author : zhoujibo
--Date   : 2015/5/22
--此文件由[BabeLua]插件自动生成


module('UBBTagConvert',package.seeall)






--将ubb字串转换成一颗ubb语法树
--[[ubb语法树结构
一个节点的结构
type 'tag'|'text'|'group'
当type为text时
    text=文本内容
当type为tag时
    name = tag名称
    params=参数列表
    body=tag内部的节点列表
当type为group时
    body = group成员
]]
--[[关于标签定义类别：
--文字类：
    type = 'text'
    文字类别的对象有如下属性域:
        color 文字颜色(0xAARRGGBB,默认白色0xFFFFFFFF)
        size 字体大小(默认20)
        font ttf字体(默认DEFAULT_TTF_PATH)
        outline 描边大小(默认0)
        outlineColor 描边颜色(0xAARRGGBB,默认白色0xFFFFFFFF)
        text 文本内容（可省略)，当具备此项的时候 [tag]xxxx[/tag]会直接转成内容，忽略xxxx的部分
--资源类：
    type = 'res'
    资源类对象有如下属性域：
        scheme 资源类型
        url 资源位置(可省略）
        params 参数列表(可省略)
--组标签：
    type = 'group'
--分行标签
    type = 'li'
        ref 引用的其他图片标签的名称
--缩放标签
    type = 'scale'
        sX 横向拉伸(1)
        sY 纵向拉伸(1)
--交互标签
    type = 'touch'
        id 交互用id
--段落标签
    type = 'para'
        align 水平对齐(left)/right/center
--行对齐标签
    type = 'linealign'
        align 行对齐(bottom)/top/center
--引用类：
    type = 'ref'
        name 引用的其他标签的名称
--
]]
function convertUBBString(str,customTag)
    local root = {type='tag',name='default',body={}}
    str = utf8.breakApart(str)
    local state = 1
    --[[
    1：阅读文本
    2：阅读[之后的字符
    3：阅读[]之中的内容
    4：阅读[/]之中的内容
    
   ]]
    local tagStack = {}
    local text = {}
    local currentTag = root
    for i,currentChar in ipairs(str)do
        if state == 1 then
            if currentChar == '[' then
                state = 2
            else
                table.insert(text,currentChar)
            end
        elseif state==2 then
            if currentChar == '[' then
                table.insert(text,currentChar)
                state = 1
            elseif currentChar == ']' then
                table.insert(tagStack,currentTag)
                if #text>0 then
                    table.insert(currentTag.body,{type='text',text=table.concat(text)})
                end
                currentTag = {type='tag',name='',params={},body = {}}
                text = {}
                state = 1
            else
                if #text~=0 then
                    table.insert(currentTag.body,{type='text',text=table.concat(text)})
                end
                if currentChar == '/' then
                    state = 4
                    text = {}
                else
                    text = {currentChar}
                    state = 3
                end
            end
        elseif state == 3 then
            if currentChar == ']' then
                local tagText = string.trim(table.concat(text))
                local isEndTag = string.sub(tagText,-1)=='/'
                if isEndTag then
                    tagText = string.sub(tagText,1,-2)
                end
                local arr = string.split(tagText,',')
                for i,s in ipairs(arr)do
                    arr[i] = string.trim(s)
                end
                local name = table.remove(arr,1)
                if name then
                    name = string.trim(name)
                else
                    name = ''
                end
                for k,v in ipairs(arr)do
                    arr[k] = string.trim(v)
                end
                if not isEndTag then
                    table.insert(tagStack,currentTag)
                end
                local tagStyle = UBBStylePlugin.getStyle(name,customTag)
                if tagStyle.type == 'group' then
                    assert(not isEndTag,'[GROUPTYPE/] is illegal!')
                    currentTag = {type='group',name=name,body={}}
                else
                    if isEndTag then
                        --单标签直接收尾
                        local endTag = {type='tag',name=name,params=arr,body={}}
                        table.insert(currentTag.body,endTag)
                        --对于[xxxx/]形式的预设文字段的处理
                        if(tagStyle.type=='text' and tagStyle.text)then
                            table.insert(endTag.body,{type='text',text=tagStyle.text})
                        end
                    else
                        local newTag =  {type='tag',name=name,params=arr,body = {}}
                        currentTag = newTag
                    end
                end
                text = {}
                state = 1

            else
                table.insert(text,currentChar)
            end
        elseif state == 4 then
            if currentChar == ']' then
                assert(string.trim(table.concat(text))==currentTag.name,'tag name not match!')
                local parentTag = table.remove(tagStack)
                if(UBBStylePlugin.TAG_CONVERT_DICT[currentTag.name])then
                    --宏标签处理
                    local url = nil
                    if currentTag.body and currentTag.body[1].type == 'text' then
                        url = currentTag.body[1].text
                    end
                    local params = currentTag.params
                    local ubbstr = UBBStylePlugin.TAG_CONVERT_DICT[currentTag.name](params,url)
                    local convertRoot = convertUBBString(ubbstr)
                    for _,v in ipairs(convertRoot.body)do
                        table.insert(parentTag.body,v)
                    end
                else
                    table.insert(parentTag.body,currentTag)
                end
                currentTag = parentTag
                state = 1
                text = {}
            else
                table.insert(text,currentChar)
            end
        end
        
    end
    assert(state==1,'malformed ubb text!')
    if #text ~= 0 then
        table.insert(currentTag.body,{type='text',text=table.concat(text)})
    end
    return root
end

--将ubb语法树转成元素列表

function convertUBBAstToElements(root,customTag)
    local tagStack = {{root,0}} --[tag结构，处理到body第几个位置]
    local currentTag = root
    local bodyIndex = 1
    local elements = {}

    local currentTouchID = nil
    local touchStack = {}
    local elementTouchDict = {}

    --需要touchid的调用此方法，不需要的直接加
    local function addToElements(element,elementList)
        table.insert(elementList or elements,element)
        elementTouchDict[element] = currentTouchID
    end

    while true do
        local tag = currentTag.body[bodyIndex]
        if not tag then
            local finishedTag,_ = unpack(table.remove(tagStack))
            local tagStyle = UBBStylePlugin.getStyle(finishedTag.name,customTag)
            local tagStyleType = tagStyle.type
            if tagStyleType == 'li' then
                --li tag 要压入li结束元素
                table.insert(elements,{UBBElement.TYPE_LI_END})
            elseif tagStyleType =='scale' then
                table.insert(elements,{UBBElement.TYPE_SCALE_END})
            elseif tagStyleType =='touch' then
                currentTouchID = table.remove(touchStack)
            elseif tagStyleType == 'para' then
                table.insert(elements,{UBBElement.TYPE_PARA_END})
            elseif tagStyleType == 'linealign' then
                table.insert(elements,{UBBElement.TYPE_LINEALIGN_END})
            end
            if finishedTag == root then
                break
            end
            local ts = tagStack[#tagStack]
            currentTag = ts[1]
            bodyIndex = ts[2]
        elseif tag.type == 'text' then
            local elementStyle = nil
            --遇到文本的段落，则根据tagStack套用样式
            if #tagStack == 1 then
                elementStyle = UBBStylePlugin.getStyle(currentTag.name,customTag)
            else
                --处理嵌套的样式
                elementStyle = {}
                for _,parentTag in ipairs(tagStack)do
                    parentTag = parentTag[1]
                    local textStyle = nil
                    if parentTag.textStyle then
                        textStyle = parentTag.textStyle
                    else
                        textStyle = UBBStylePlugin.getStyle(parentTag.name,customTag)
                    end
                    for k,v in pairs(textStyle)do
                        elementStyle[k] = v
                    end
                end
            end
            addToElements({UBBElement.TYPE_TEXT,elementStyle.text or tag.text,elementStyle.font,elementStyle.fontsize,
            elementStyle.color,elementStyle.outlineColor,elementStyle.outline})
        elseif tag.type == 'tag' then
            local tagStyle = UBBStylePlugin.getStyle(tag.name,customTag)
            if tag.name == 'text' then
                --如果是[text]组织它的文本样式
                local elementStyle = {}
                local params = tag.params
                elementStyle.font = params[1]
                if params[2] then
                    elementStyle.fontsize = tonumber(params[2])
                end
                if params[3] then
                    elementStyle.color = tonumber(params[3])
                end
                if params[4] then
                    elementStyle.outlineColor = tonumber(params[4])
                end
                if params[5] then
                    elementStyle.outline = tonumber(params[5])
                end
                tag.textStyle = elementStyle
            end

            local tagStyleType = tagStyle.type
            if tagStyleType == 'res' then
                --资源类的不可嵌套的tag调用
                local url = tagStyle.url
                if(not url)and tag.body[1] and tag.body[1].type =='text' then
                    url = tag.body[1].text
                end
                local params = tag.params
                if(not params)then
                    params = tagStyle.params
                end
                if(not params)then
                    params = {}
                end

                addToElements({UBBElement.TYPE_RES,tagStyle.scheme,url,params})
            elseif tagStyleType == 'text' 
                or tagStyleType =='li' 
                or tagStyleType =='scale' 
                or tagStyleType == 'touch' 
                or tagStyleType == 'para'
                or tagStyleType == 'linealign' then
                --可嵌套内容的tag类，进行压栈处理
                tagStack[#tagStack][2] = bodyIndex
                table.insert(tagStack,{tag,0})
                currentTag = tag
                bodyIndex = 0
                if tagStyleType=='li' then
                    
                    --li tag压栈时要加入li开始元素,和li所指向的图片元素
                    table.insert(elements,{UBBElement.TYPE_LI_START})
                    local liRefTagName = tagStyle.ref
                    local liRefTagStyle = UBBStylePlugin.getStyle(liRefTagName,customTag)
                    assert(liRefTagStyle.type=='res' and liRefTagStyle.scheme=='img','li only support img tag by far!')
                    local url = liRefTagStyle.url
                    assert(url,'no picture url for li start tag!')
                    local params = liRefTagStyle.params or {}
                    addToElements({UBBElement.TYPE_RES,liRefTagStyle.scheme,url,params})
                elseif tagStyleType=='scale' then
                    local params = tag.params
                    local sX = tonumber(params[1] or tagStyle.sX or 1)
                    local sY = tonumber(params[2] or tagStyle.sY or 1)
                    assert(sX>0 and sY>0,'scale param must larger than zero!')
                    table.insert(elements,{UBBElement.TYPE_SCALE_START,sX,sY})
                elseif tagStyleType == 'touch' then
                    table.insert(touchStack,currentTouchID)
                    local params = tag.params
                    if params then
                        currentTouchID = table.concat(params,',')
                    elseif tagStyle.id then
                        currentTouchID = tagStyle.id
                    else
                        assert(false,'touch id not setted!')
                    end
                elseif tagStyleType == 'para' then
                    local params = tag.params
                    local align = params[1] or tagStyle.align or 'left'
--                    local lineAlign = params[2] or tagStyle.lineAlign or 'bottom'
                    assert(align=='left' or align == 'center' or align =='right','invalid align type!')
                    if align == 'left' then
                        align = cc.TEXT_ALIGNMENT_LEFT
                    elseif align == 'center' then
                        align = cc.TEXT_ALIGNMENT_CENTER
                    elseif align == 'right' then
                        align = cc.TEXT_ALIGNMENT_RIGHT
                    end
                    table.insert(elements,{UBBElement.TYPE_PARA_START,align})
                elseif tagStyleType == 'linealign' then
                    local params = tag.params
                    local lineAlign = params[1] or tagStyle.align or 'bottom'
                    assert(lineAlign=='bottom' or lineAlign == 'center' or lineAlign =='top','invalid line align type!')
                    if lineAlign == 'top' then
                        lineAlign = cc.VERTICAL_TEXT_ALIGNMENT_TOP
                    elseif lineAlign == 'center' then
                        lineAlign = cc.VERTICAL_TEXT_ALIGNMENT_CENTER
                    elseif lineAlign == 'bottom' then
                        lineAlign = cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM
                    end
                    table.insert(elements,{UBBElement.TYPE_LINEALIGN_START,lineAlign})
                end
            else
                assert(false,'builtin tag or custom tag type setting illegal!')
            end
        elseif tag.type == 'group' then
            --[g][/g]
            local gElement = {UBBElement.TYPE_GROUP,{}}
            for _,gTag in ipairs(tag.body)do
                local tagStyle = UBBStylePlugin.getStyle(gTag.name,customTag)
                local url = tagStyle.url
                assert(tagStyle.type == 'res','UBB tags between [g][/g] must be type of res!')
                if(not url)and gTag.body[1] and gTag.body[1].type =='text' then
                    url = gTag.body[1].text
                end
                addToElements({UBBElement.TYPE_RES,tagStyle.scheme,url,gTag.params},gElement[2])
                
            end
            table.insert(elements,gElement)
        else
            assert(false,'unknown tag type!')
        end
        bodyIndex = bodyIndex+1
    end
    return elements,elementTouchDict
end

